//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

//------------------------------------------------------------------------------
//
// This file implements BootStore interface for NAND storage. Implementation
// uses FMD interface for physical media access.
//
#include <windows.h>
#include <oal.h>
#include <boot.h>
#include <pcireg.h>
#include <bootpart.h>
#include <fmd.h>

//------------------------------------------------------------------------------

#define SECTOR_MAX_SIZE         2048
#define BLOCK_MAX_SIZE          0x20000

#define PARTITIONS_IN_MBR       4

#define INVALID_POS             (-1)
#define INVALID_SECTOR          (-1)
#define INVALID_BLOCK           (-1)
#define INVALID_REGION          (-1)

//------------------------------------------------------------------------------

enum {
    NAND_MODE_NONE = 0,
    NAND_MODE_INIT,
    NAND_MODE_OPEN_BINARY,
    NAND_MODE_OPEN_RESERVED,
    NAND_MODE_OPEN_PARTITION
};

enum {
    REGION_TYPE_XIP = 0,
    REGION_TYPE_RO,
    REGION_TYPE_RW,
    REGION_TYPE_BIN = 64,
    REGION_TYPE_RESERVED = 65
};

//------------------------------------------------------------------------------

#define NAND_STORE_COOKIE       'Nand'

//------------------------------------------------------------------------------

typedef struct NAND_REGION {
    UINT32 type;
    UINT32 block;
    UINT32 blocks;
    UINT32 compBlocks;
    CHAR   name[8];
    UINT32 phBlock;
    UINT32 falBaseSector;
    UINT32 firstLgSector;
    UINT32 lastLgSector;
    UINT32 emptyLgSector;
    UINT32 firstSector;
    UINT32 lastSector;
} NAND_REGION;

typedef struct NAND_PARTITION {
    UINT32 type;
    UINT32 sector;
    UINT32 sectors;
    UINT32 region;
    UINT32 offset;
} NAND_PARTITION;

typedef struct NAND_POS {
    UINT32 block;
    UINT32 phBlock;
} NAND_POS;

//------------------------------------------------------------------------------

typedef struct NAND_STORE {

    DWORD  cookie;                          // Cookie to verify handler

    UINT32 baseAddress;                     // Base physical address
    UINT32 binSize[8];                      // Binary volumes sizes

    HANDLE hFmd;                            // FMD handle

    UINT32 sectorSize;                      // Sector size
    UINT32 sectorsPerBlock;                 // Sectors per block
    UINT32 blockSize;                       // Sector size * sectors per block

    UINT32 phBlocksOnFlash;                 // Physical blocks on flash
    UINT32 blocksOnFlash;                   // Usable blocks on flash

    UINT32 binBlocks;                       // Binary volumes size in blocks
    UINT32 mbrBlock;                        // MBR block

    UINT32 openAccess;                      // Actual access mode
    UINT32 openMode;                        // In which mode store is opened
    UINT32 openRegion;                      // Open region
    UINT32 openBaseSector;                  // Offset in region
    UINT32 openSize;                        // Size fo read/write/seek operation
    UINT32 openPos;                         // Actual position

    UINT32 regions;                         // Number of regions on disk
    NAND_REGION region[32];                 // Regions descriptors

    UINT32 partitions;
    NAND_PARTITION partition[4];

    NAND_POS pos;

    UINT8 firstSector[SECTOR_MAX_SIZE];     // Buffers for sectors
    UINT8 lastSector[SECTOR_MAX_SIZE];

#ifndef STORE_READONLY
    UINT8 buffer[BLOCK_MAX_SIZE];           // Buffer needed for write/erase
#endif
} NAND_STORE;

//------------------------------------------------------------------------------

static NAND_STORE s_store;

//------------------------------------------------------------------------------

BOOL
BootStoreNandDeinit(
    HANDLE hStore
    );

BOOL
BootStoreNandIoCtl(
    HANDLE hStore,
    DWORD code,
    VOID* pBuffer,
    DWORD size
    );

BOOL
BootStoreNandOpen(
    HANDLE hStore,
    DWORD access,
    DWORD mode,
    VOID *pInfo,
    DWORD infoSize
    );

BOOL
BootStoreNandClose(
    HANDLE hStore
    );

UINT64
BootStoreNandGetSize(
    HANDLE hStore
    );

UINT32
BootStoreNandRead(
    HANDLE hStore,
    VOID* pBuffer,
    UINT32 size
    );

UINT32
BootStoreNandWrite(
    HANDLE hStore,
    VOID* pBuffer,
    UINT32 size
    );

BOOL
BootStoreNandSeek(
    HANDLE hStore,
    UINT64 position
    );

BOOL
BootStoreNandGetPos(
    HANDLE hStore,
    UINT64 *pPosition
    );

BOOL
MountStore(
    NAND_STORE *pStore
    );

BOOL
LockRange(
    NAND_STORE *pStore,
    UINT32 base,
    UINT32 size,
    BOOL lock
    );

BOOL
OpenBinary(
    NAND_STORE* pStore,
    BOOT_STORE_OPEN_BINARY_REGION* pInfo
    );

BOOL
OpenReservedRegion(
    NAND_STORE* pStore,
    BOOT_STORE_OPEN_RESERVED_REGION* pInfo
    );

BOOL
OpenPartition(
    NAND_STORE* pStore,
    BOOT_STORE_OPEN_PARTITION* pInfo
    );

BOOL
Read(
    NAND_STORE *pStore,
    UCHAR* pBuffer,
    UINT32 size
    );

BOOL
Write(
    NAND_STORE *pStore,
    UCHAR* pBuffer,
    UINT32 size
    );

BOOL
Format(
    NAND_STORE* pStore,
    BOOT_STORE_FORMAT_INFO* pInfo
    );

BOOL
GetSize(
    NAND_STORE* pStore,
    BOOT_STORE_GETSIZE_PARAMS *pParams
    );

BOOL
Seek(
    NAND_STORE* pStore,
    BOOT_STORE_SEEK_PARAMS *pParams
    );

BOOL
GetPos(
    NAND_STORE* pStore,
    BOOT_STORE_GETPOS_PARAMS *pParams
    );

//------------------------------------------------------------------------------

static BOOT_DEVICE_IFC s_nandIfc = {
    &s_store,
    BootStoreNandDeinit,
    BootStoreNandOpen,
    BootStoreNandClose,
    BootStoreNandRead,
    BootStoreNandWrite,
    BootStoreNandIoCtl
};

//------------------------------------------------------------------------------

BOOL
BootStoreNandInit(
    VOID* pContext,
    DWORD contextSize,
    BOOT_DEVICE_IFC **ppIfc
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = NULL;
    BOOT_STORE_NAND_INIT_CONTEXT* pInfo = pContext;
    PCI_REG_INFO regInfo;
    FlashInfo flashInfo;

    // Check parameter
    if (ppIfc == NULL) goto cleanUp;
    *ppIfc = NULL;

    // Check if we get information we need
    if ((pContext == NULL) || (contextSize < sizeof(BOOT_STORE_NAND_INIT_CONTEXT)))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandInit: "
            L"Invalid context to init NAND store!\r\n"
            ));
        goto cleanUp;
        }

    // Create store context
    pStore = &s_store;
    memset(pStore, 0, sizeof(NAND_STORE));
    pStore->cookie = NAND_STORE_COOKIE;

    // Save base address and binary partition sizes
    pStore->baseAddress = pInfo->baseAddress;
    memcpy(pStore->binSize, pInfo->binPartSize, sizeof(pStore->binSize));

    // Open FMD to access NAND
    regInfo.MemBase.Reg[0] = pInfo->baseAddress;
    pStore->hFmd = FMD_Init(NULL, &regInfo, NULL);
    if (pStore->hFmd == NULL)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandInit: "
            L"FMD_Init call failed!\r\n"
            ));
        goto cleanUp;
        }

    // Get flash info
    if (!FMD_GetInfo(&flashInfo))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandInit: "
            L"FMD_GetInfo call failed!\r\n"
            ));
        goto cleanUp;
        }

    // Check if we can support this FMD
    if (flashInfo.wDataBytesPerSector > SECTOR_MAX_SIZE)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandInit: "
            L"Maximal supported sector size is %d!\r\n", SECTOR_MAX_SIZE
            ));
        goto cleanUp;
        }

    // Save flash geometry info
    pStore->sectorSize = flashInfo.wDataBytesPerSector;
    pStore->sectorsPerBlock = flashInfo.wSectorsPerBlock;
    pStore->phBlocksOnFlash = flashInfo.dwNumBlocks;
    pStore->blockSize = pStore->sectorSize * pStore->sectorsPerBlock;

    if (!MountStore(pStore)) goto cleanUp;

    // We are done
    pStore->openMode = NAND_MODE_INIT;
    *ppIfc = &s_nandIfc;
    rc = TRUE;

cleanUp:
    if (!rc && (pStore != NULL)) BootStoreNandDeinit(pStore);
    return rc;
}

//------------------------------------------------------------------------------

BOOL
BootStoreNandDeinit(
    HANDLE hStore
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = (NAND_STORE*)hStore;


    // Check store handle
    if ((pStore == NULL) || (pStore->cookie != NAND_STORE_COOKIE))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandDeinit: "
            L"Invalid store handle!\r\n"
            ));
        goto cleanUp;
        }

    // If store is opened, close it first
    if (pStore->openMode != NAND_MODE_INIT)
        {
        BootStoreNandClose(hStore);
        }

    if (pStore->hFmd != NULL) FMD_Deinit(pStore->hFmd);

    // Delete context (oops, clear it only)
    memset(pStore, 0, sizeof(NAND_STORE));

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
BootStoreNandOpen(
    HANDLE hStore,
    DWORD access,
    DWORD mode,
    VOID *pInfo,
    DWORD infoSize
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = (NAND_STORE*)hStore;


    // Check store handle
    if ((pStore == NULL) || (pStore->cookie != NAND_STORE_COOKIE))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
            L"Invalid store handle!\r\n"
            ));
        goto cleanUp;
        }

    // We can call any open function only when store is in INIT mode
    if (pStore->openMode != NAND_MODE_INIT)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
            L"Store is in invalid open mode (%d)!\r\n", pStore->openMode
            ));
        goto cleanUp;
        }

#ifndef STORE_READONLY
    // We only support read or write mode, but not both
    if (((access & BOOT_DEVICE_ACCESS_READ) != 0) &&
        ((access & BOOT_DEVICE_ACCESS_WRITE) != 0))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
            L"Invalid access mode (%08x)!\r\n", access
            ));
        goto cleanUp;
        }
#else
    // We only support read or write mode, but not both
    if ((access & BOOT_DEVICE_ACCESS_READ) == 0)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
            L"Invalid access mode (%08x)!\r\n", access
            ));
        goto cleanUp;
        }
#endif

    switch (mode)
        {
        case BOOT_STORE_OPEN_MODE_BINARY_REGION:
            {
            BOOT_STORE_OPEN_BINARY_REGION* pModeInfo = pInfo;
            if ((pModeInfo == NULL) ||
                (infoSize < sizeof(BOOT_STORE_OPEN_BINARY_REGION)))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
                    L"Invalid parameters for binary region mode!\r\n"
                    ));
                goto cleanUp;
                }
            rc = OpenBinary(pStore, pModeInfo);
            }
            break;
        case BOOT_STORE_OPEN_MODE_RESERVED_REGION:
            {
            BOOT_STORE_OPEN_RESERVED_REGION* pModeInfo = pInfo;
            if ((pModeInfo == NULL) ||
                (infoSize < sizeof(BOOT_STORE_OPEN_RESERVED_REGION)))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
                    L"Invalid parameters for reserved region mode!\r\n"
                    ));
                goto cleanUp;
                }
            rc = OpenReservedRegion(pStore, pModeInfo);
            }
            break;
        case BOOT_STORE_OPEN_MODE_PARTITION:
            {
            BOOT_STORE_OPEN_PARTITION* pModeInfo = pInfo;
            if ((pModeInfo == NULL) ||
                (infoSize < sizeof(BOOT_STORE_OPEN_PARTITION)))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandOpen: "
                    L"Invalid mode info parameters for partition mode!\r\n"
                    ));
                goto cleanUp;
                }
            rc = OpenPartition(pStore, pModeInfo);
            }
            break;
        }

    if (rc)
        {
        pStore->openAccess = access;
        }

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
BootStoreNandClose(
    HANDLE hStore
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = (NAND_STORE*)hStore;


    // Check store handle
    if ((pStore == NULL) || (pStore->cookie != NAND_STORE_COOKIE))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandClose: "
            L"Invalid store handle!\r\n"
            ));
        goto cleanUp;
        }

    // We can call any open function only when store is in INIT mode.
    if (pStore->openMode <= NAND_MODE_INIT)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandClose: "
            L"Store is in invalid open mode (%d)!\r\n", pStore->openMode
            ));
        goto cleanUp;
        }

    pStore->openMode = NAND_MODE_INIT;
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

UINT32
BootStoreNandRead(
    HANDLE hStore,
    VOID* pBuffer,
    UINT32 size
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = (NAND_STORE*)hStore;

    // Check store handle
    if ((pStore == NULL) || (pStore->cookie != NAND_STORE_COOKIE))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandClose: "
            L"Invalid store handle!\r\n"
            ));
        goto cleanUp;
        }

    // At least buffer should not be NULL
    if ((pBuffer == NULL) || (size == 0))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandRead: "
            L"Invalid parameters!\r\n"
            ));
        goto cleanUp;
        }

    // Check for access
    if ((pStore->openAccess & BOOT_DEVICE_ACCESS_READ) == 0)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandRead: "
            L"Store isn't open with read access\r\n"
            ));
        goto cleanUp;
        }

    // Check for read size
    if ((size > pStore->openSize) ||
        ((pStore->openSize - size) < pStore->openPos))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandRead: "
            L"Read spans over open region/partition boundary!\r\n"
            ));
        goto cleanUp;
        }

    switch (pStore->openMode)
        {
        case NAND_MODE_OPEN_BINARY:
        case NAND_MODE_OPEN_RESERVED:
        case NAND_MODE_OPEN_PARTITION:
            rc = Read(pStore, pBuffer, size);
            break;
        default:
            goto cleanUp;
        }

cleanUp:
    return (rc ? size : -1);
}

//------------------------------------------------------------------------------

#ifndef STORE_READONLY

UINT32
BootStoreNandWrite(
    HANDLE hStore,
    VOID* pBuffer,
    UINT32 size
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = (NAND_STORE*)hStore;


    // Check store handle
    if ((pStore == NULL) || (pStore->cookie != NAND_STORE_COOKIE))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandWrite: "
            L"Invalid store handle!\r\n"
            ));
        goto cleanUp;
        }

    // At least buffer should not be NULL
    if ((pBuffer == NULL) || (size == 0))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandWrite: "
            L"Invalid parameters!\r\n"
            ));
        goto cleanUp;
        }

    // Check for access
    if ((pStore->openAccess & BOOT_DEVICE_ACCESS_WRITE) == 0)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandWrite: "
            L"Store isn't open with write access!\r\n"
            ));
        goto cleanUp;
        }

    // Check for write size
    if ((size > pStore->openSize) ||
        ((pStore->openSize - size) < pStore->openPos))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandWrite: "
            L"Write spans over open region/partition boundary!\r\n"
            ));
        goto cleanUp;
        }

    switch (pStore->openMode)
        {
        case NAND_MODE_OPEN_BINARY:
        case NAND_MODE_OPEN_RESERVED:
        case NAND_MODE_OPEN_PARTITION:
            rc = Write(pStore, pBuffer, size);
            break;
        default:
            goto cleanUp;
        }

cleanUp:
    return (rc ? size : -1);
}

#else

UINT32
BootStoreNandWrite(
    HANDLE hStore,
    VOID* pBuffer,
    UINT32 size
    )
{
    return -1;
}

#endif

//------------------------------------------------------------------------------

BOOL
BootStoreNandIoCtl(
    HANDLE hStore,
    DWORD code,
    VOID* pBuffer,
    DWORD size
    )
{
    BOOL rc = FALSE;
    NAND_STORE* pStore = (NAND_STORE*)hStore;


    // Check store handle
    if ((pStore == NULL) || (pStore->cookie != NAND_STORE_COOKIE))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl: "
            L"Invalid store handle!\r\n"
            ));
        goto cleanUp;
        }

    // Store must be at least initialized
    if (pStore->openMode == NAND_MODE_NONE)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl: "
            L"Store must be at least initialized!\r\n"
            ));
        goto cleanUp;
        }

    switch (code)
        {
#ifndef STORE_READONLY
        case BOOT_STORE_IOCTL_FORMAT:
            // Check input parameter
            if (pBuffer == NULL || size < sizeof(BOOT_STORE_FORMAT_INFO))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl!"
                    L"BOOT_STORE_IOCTL_FORMAT: Invalid parameters!\r\n"
                    ));
                break;
                }
            if (pStore->openMode != NAND_MODE_INIT)
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl!"
                    L"BOOT_STORE_IOCTL_FORMAT: Invalid storage mode!\r\n"
                    ));
                break;
                }
            rc = Format(pStore, (BOOT_STORE_FORMAT_INFO*)pBuffer);
            break;
#endif
        case BOOT_STORE_IOCTL_GETSIZE:
            // Check parameters
            if (pBuffer == NULL || size != sizeof(BOOT_STORE_GETSIZE_PARAMS))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl!"
                    L"BOOT_STORE_IOCTL_GETSIZE: Invalid parameters!\r\n"
                    ));
                break;
                }
            rc = GetSize(pStore, (BOOT_STORE_GETSIZE_PARAMS*)pBuffer);
            break;
        case BOOT_STORE_IOCTL_GETPOS:
            // Check parameters
            if (pBuffer == NULL || size != sizeof(BOOT_STORE_GETPOS_PARAMS))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl!"
                    L"BOOT_STORE_IOCTL_GETPOS: Invalid parameters!\r\n"
                    ));
                break;
                }
            rc = GetPos(pStore, (BOOT_STORE_GETPOS_PARAMS*)pBuffer);
            break;
        case BOOT_STORE_IOCTL_SEEK:
            // Check parameters
            if (pBuffer == NULL || size != sizeof(BOOT_STORE_SEEK_PARAMS))
                {
                OALMSG(OAL_ERROR, (L"ERROR: BootStoreNandIoCtl!"
                    L"BOOT_STORE_IOCTL_SEEK: Invalid parameters!\r\n"
                    ));
                break;
                }
            rc = Seek(pStore, (BOOT_STORE_SEEK_PARAMS*)pBuffer);
            break;
        }

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
GetFlashRegionInfo(
    NAND_STORE *pStore,
    UINT8 *pSector,
    UINT32 index,
    UINT32 *pType,
    UINT32 *pSize,
    UINT32 *pCompactSize
    )
{
    BOOL rc = FALSE;
    FlashLayoutSector* pLayout = (FlashLayoutSector*)pSector;
    UINT8* pInfo = (UINT8*)&pLayout[1];
    FlashRegion* pRegion;
    UINT32 regions;


    // Check from layout sector
    if (memcmp(pSector, FLASH_LAYOUT_SIG, FLASH_LAYOUT_SIG_SIZE) != 0)
        goto cleanUp;

    // Get number of regions defined in layout sector
    regions = pLayout->cbRegionEntries/sizeof(FlashRegion);
    if (index >= regions) goto cleanUp;

    // Get region structure
    pRegion = &((FlashRegion*)&pInfo[pLayout->cbReservedEntries])[index];

    // Copy information
    *pType = pRegion->regionType;
    *pSize = pRegion->dwNumLogicalBlocks;
    *pCompactSize = pRegion->dwCompactBlocks;

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
GetReservedRegionInfo(
    NAND_STORE *pStore,
    UINT8 *pSector,
    UINT32 index,
    LPSTR  pNameBuffer,
    UINT32 nameBufferSize,
    UINT32 *pBlock,
    UINT32 *pBlocks

    )
{
    BOOL rc = FALSE;
    FlashLayoutSector* pLayout = (FlashLayoutSector*)pSector;
    UINT8* pInfo = (UINT8*)&pLayout[1];
    ReservedEntry* pEntry;
    UINT32 entries;

    // Check from layout sector
    if (memcmp(pSector, FLASH_LAYOUT_SIG, FLASH_LAYOUT_SIG_SIZE) != 0)
        goto cleanUp;

    // Get number of reserved regions
    entries = pLayout->cbReservedEntries/sizeof(ReservedEntry);

    if (index >= entries) goto cleanUp;
    pEntry = &((ReservedEntry*)&pInfo[0])[index];

    // Copy name
    PREFAST_SUPPRESS(419, "nameBufferSize is pNameBuffer size")
    strncpy(pNameBuffer, pEntry->szName, nameBufferSize);
    pNameBuffer[nameBufferSize - 1] = '\0';
    *pBlock = pEntry->dwStartBlock;
    *pBlocks = pEntry->dwNumBlocks;

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
GetPartitionInfo(
    NAND_STORE *pStore,
    UINT8 *pSector,
    UINT32 index,
    UINT32 *pType,
    UINT32 *pStartSector,
    UINT32 *pSectors
    )
{
    BOOL rc = FALSE;
    PARTENTRY table[PARTITIONS_IN_MBR];
    UINT ix;

    // Check for MBR
    if ((*(UINT16*)&pSector[BOOT_SIZE - 2] != BOOTSECTRAILSIGH) ||
        ((pSector[0] != BS2BYTJMP) && (pSector[0] != BS3BYTJMP)))
        goto cleanUp;

    // Copy partition table to avoid alignment issue
    memcpy(table, &pSector[BOOT_SIZE - 2 - sizeof(table)], sizeof(table));

    ix = 0;
    while (ix < PARTITIONS_IN_MBR)
        {
        if (table[ix].Part_FileSystem != 0)
            {
            if (index == 0) break;
            index--;
            }
        ix++;
        }
    if (ix >= PARTITIONS_IN_MBR) goto cleanUp;

    *pType = table[ix].Part_FileSystem;
    *pStartSector = table[ix].Part_StartSector;
    *pSectors = table[ix].Part_TotalSectors;

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
RegionForSector(
    NAND_STORE *pStore,
    UINT32 sector,
    UINT32 *pId,
    UINT32 *pSector
    )
{
    BOOL rc = FALSE;
    UINT32 region;
    UINT32 regions = pStore->regions;
    UINT32 sectorsPerBlock = pStore->sectorsPerBlock;
    UINT32 sectors;
    NAND_REGION *pRegion = pStore->region;


    region = 0;
    while (region < regions)
        {
        if (pRegion->block < pStore->mbrBlock)
            {
            region++;
            pRegion++;
            continue;
            }
        sectors = (pRegion->blocks - pRegion->compBlocks) * sectorsPerBlock;
        if (sector < sectors) break;
        sector -= sectors;
        region++;
        pRegion++;
        }
    if (region < regions)
        {
        *pId = region;
        *pSector = sector;
        rc = TRUE;
        }
    return rc;
}

//------------------------------------------------------------------------------

BOOL
MountStore(
    NAND_STORE *pStore
    )
{
    BOOL rc = FALSE;
    UINT32 regions, partitions;
    UINT32 block, phBlock;
    UINT32 phBinBlocks;
    UINT32 sectorSize, sectorsPerBlock, blockSize;
    NAND_REGION *pRegion;
    NAND_PARTITION *pPartition;
    UINT32 ix;

    // Shortcuts
    sectorSize = pStore->sectorSize;
    sectorsPerBlock = pStore->sectorsPerBlock;
    blockSize = pStore->blockSize;

    // Initialize
    pStore->blocksOnFlash = 0;
    pStore->mbrBlock = INVALID_BLOCK;
    pStore->regions = 0;
    memset(pStore->region, 0, sizeof(pStore->region));
    pStore->pos.block = 0;
    pStore->pos.phBlock = 0;

    regions = 0;
    pRegion = pStore->region;

    // Get binary regions sizes
    block = 0;
    for (ix = 0; ix < dimof(pStore->binSize); ix++)
        {
        UINT32 blocks;

        if (pStore->binSize[ix] == 0) break;
        if (regions >= dimof(pStore->region)) break;

        blocks = (pStore->binSize[ix] + blockSize - 1)/blockSize;
        pRegion[regions].type = REGION_TYPE_BIN;
        pRegion[regions].block = block;
        pRegion[regions].blocks = blocks;
        pRegion[regions].phBlock = INVALID_BLOCK;
        pRegion[regions].falBaseSector = INVALID_SECTOR;
        block += blocks;
        regions++;
        }
    pStore->binBlocks = block;

    // Walk over store to find bad blocks and layout
    block = 0;
    phBlock = 0;
    while (phBlock < pStore->phBlocksOnFlash)
        {
        // Skip bad blocks
        DWORD status = FMD_GetBlockStatus(phBlock);
        if ((status & BLOCK_STATUS_BAD) != 0)
            {
            phBlock++;
            continue;
            }
        
        // Check for reserved flag on binary
        if (((status & BLOCK_STATUS_RESERVED) != 0) &&
            (block >= pStore->binBlocks))
            {
            OALMSG(OAL_WARN, (L"BootStoreNand!MountStore: "
                L"Block %d outside binary regions is marked as reserved!\r\n",
                phBlock
                ));
            }
        else if (((status & BLOCK_STATUS_RESERVED) == 0) &&
            (block < pStore->binBlocks))
            {
            OALMSG(OAL_WARN, (L"BootStoreNand!MountStore: "
                L"Block %d inside binary regions isn't marked as reserved!\r\n",
                phBlock
                ));
            }

        // Update binary partitions info
        if (block < pStore->binBlocks)
            {
            for (ix = 0; ix < regions; ix++)
                {
                if (pRegion[ix].block == block) pRegion[ix].phBlock = phBlock;
                if (pRegion[ix].block >= block) break;
                }
            phBlock++;
            block++;
            continue;
            }
        else if (block == pStore->binBlocks)
            {
            // Save physical block and sector for later use
            phBinBlocks = phBlock;
            }

        // Try to find MBR/Layout sectors
        if (pStore->mbrBlock == INVALID_BLOCK)
            {
            UINT8* pMbr = pStore->firstSector;
            UINT8* pLayout = pStore->lastSector;
            UINT32 phSector, baseBlock, falBaseSector;

            phSector = phBlock * sectorsPerBlock;
            if (!FMD_ReadSector(phSector, pMbr, NULL, 1)) goto cleanUp;
            if (!BootStoreIsThisMBR(pStore->sectorSize, pMbr))
                {
                phBlock++;
                block++;
                continue;
                }
            if (!FMD_ReadSector(phSector + 1, pLayout, NULL, 1)) goto cleanUp;
            if (!BootStoreIsThisLayoutSector(pStore->sectorSize, pLayout))
                {
                phBlock++;
                block++;
                continue;
                }

            // Actual block is MBR one
            pStore->mbrBlock = block;

            // Add reserved regions to partition table

            ix = 0;
            while (regions < dimof(pStore->region))
                {
                // Get info
                if (!GetReservedRegionInfo(
                    pStore, pLayout, ix, pRegion[regions].name,
                    sizeof(pRegion[regions].name), &pRegion[regions].block,
                    &pRegion[regions].blocks
                    )) break;
                // Update info as start block is relative
                pRegion[regions].block += pStore->binBlocks;
                pRegion[regions].type = REGION_TYPE_RESERVED;
                pRegion[regions].phBlock = INVALID_BLOCK;
                pRegion[regions].falBaseSector = INVALID_SECTOR;
                // Move to next region
                regions++;
                ix++;
                }

            // Add FAL flash regions to partition table
            ix = 0;
            baseBlock = block;
            falBaseSector = 0;
            while (regions < dimof(pStore->region))
                {
                // Get info
                if (!GetFlashRegionInfo(
                        pStore, pLayout, ix++, &pRegion[regions].type,
                        &pRegion[regions].blocks, &pRegion[regions].compBlocks
                        )) {
                        break;
                    }
                // Update table
                pRegion[regions].block = baseBlock;
                pRegion[regions].phBlock = INVALID_BLOCK;
                pRegion[regions].falBaseSector = falBaseSector;
                pRegion[regions].firstLgSector = 0;
                if (pRegion[regions].blocks != -1)
                    {
                    falBaseSector += pRegion[regions].blocks * sectorsPerBlock;
                    pRegion[regions].blocks += pRegion[regions].compBlocks;
                    baseBlock += pRegion[regions].blocks;
                    }
                regions++;
                // Region without size limit must be last one
                if (pRegion[regions - 1].blocks == -1) break;
                }

            pStore->regions = regions;

            // Create partitions info table
            ix = 0;
            partitions = 0;
            pPartition = pStore->partition;
            while (partitions < dimof(pStore->partition))
                {
                UINT32 sector, region;

                // Get partition info
                if (!GetPartitionInfo(
                        pStore, pMbr, ix++, &pPartition[partitions].type,
                        &pPartition[partitions].sector,
                        &pPartition[partitions].sectors
                        )) {
                        break;
                    }
                // Find region for partition start sector and base in region
                if (!RegionForSector(
                        pStore, pPartition[partitions].sector, &region, &sector
                        )) continue;
                pPartition[partitions].region = region;
                pPartition[partitions].offset = sector;
                partitions++;
                }
            pStore->partitions = partitions;

            // Move back to obtain physical blocks for reserved partitions
            phBlock = phBinBlocks;
            block = pStore->binBlocks;
            continue;
            }

        // If we know MBR & layout update region info...
        if (pStore->mbrBlock != INVALID_BLOCK)
            {
            for (ix = 0; ix < regions; ix++)
                {
                if (pRegion[ix].block == block)
                    {
                    pRegion[ix].phBlock = phBlock;
                    }
                if (pRegion[ix].block >= block) break;
                }
            }

        // Move to next block
        phBlock++;
        block++;
        }

    // Save number of usable blocks
    pStore->blocksOnFlash = block;
    
    // If we find MBR and Layout finish layout
    if ((pStore->mbrBlock != INVALID_BLOCK) && (regions > 0) &&
        (pRegion[regions - 1].blocks == -1))
        {
        UINT32 blocks = block - pRegion[regions - 1].block;
        pRegion[regions - 1].blocks = blocks;
        pRegion[regions - 1].lastLgSector = blocks * sectorsPerBlock;
        }

    // Update first/last sector info for regions
    for (ix = 0; ix < regions; ix++)
        {
        UINT32 blocks, first, count;

        if (pRegion[ix].falBaseSector != INVALID_SECTOR)
            {
            blocks = pRegion[ix].blocks - pRegion[ix].compBlocks;
            pRegion[ix].firstSector = 0;
            pRegion[ix].lastSector = blocks * sectorsPerBlock - 1;
            }
        else
            {
            pRegion[ix].firstSector = INVALID_SECTOR;
            pRegion[ix].lastSector = INVALID_SECTOR;
            }
        first = pRegion[ix].block * sectorsPerBlock;
        count = pRegion[ix].blocks * sectorsPerBlock;
        pRegion[ix].firstLgSector = first;
        pRegion[ix].lastLgSector = first + count - 1;
        pRegion[ix].emptyLgSector = INVALID_SECTOR;
        }

#ifdef BOOTSTORE_DEBUG
        {
        UINT8 *pCh;

        OALMSG(1, (L"INFO: MountStore: "
            L"Flash Region Info:\r\n"
            L"name            : "
            ));

            for (pCh = pRegion[ix].name; *pCh != 0; pCh++)
                OALMSG(1, (
                    L"%c", *pCh
                ));
        }

        OALMSG(1, (L"\r\n"
            L"index           : %d\r\n"
            L"type            : %d\r\n"
            L"block           : %d\r\n"
            L"blocks          : %d\r\n"
            L"compBlocks      : %d\r\n"
            L"phBlock         : %d\r\n"
            L"falBaseSector   : %d\r\n"
            L"firstLgSector   : %d\r\n"
            L"lastLgSector    : %d\r\n"
            L"emptyLgSector   : %d\r\n"
            L"firstSector     : %d\r\n"
            L"lastSector      : %d\r\n",
            ix,
            pRegion[ix].type,
            pRegion[ix].block,
            pRegion[ix].blocks,
            pRegion[ix].compBlocks,
            pRegion[ix].phBlock,
            pRegion[ix].falBaseSector,
            pRegion[ix].firstLgSector,
            pRegion[ix].lastLgSector,
            pRegion[ix].firstLgSector,
            pRegion[ix].emptyLgSector,
            pRegion[ix].firstSector,
            pRegion[ix].lastSector
            ));   
#endif
        

#ifdef BOOTSTORE_DEBUG
        partitions = 0;
        pPartition = pStore->partition;
        while (partitions < dimof(pStore->partition))
            {            
            OALMSG(1, (L"INFO: MountStore: "
                L"Partition Info:\r\n"
                L"index           : %d\r\n"
                L"type            : %d\r\n"
                L"sector          : %d\r\n"
                L"sectors         : %d\r\n"
                L"region          : %d\r\n",
                partitions,
                pPartition->type,
                pPartition->sector,
                pPartition->sectors,
                pPartition->region
                ));            
            partitions++;
            pPartition++;
            }
#endif

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

UINT32
MapLgToPhBlock(
    NAND_STORE *pStore,
    UINT32 block
    )
{
    UINT32 phBlock = INVALID_BLOCK;
    DWORD status;

    if (block < pStore->pos.block)
        {
        pStore->pos.block = 0;
        pStore->pos.phBlock = 0;
        }
    while (pStore->pos.block < block)
        {
        if (++pStore->pos.phBlock > pStore->phBlocksOnFlash) goto cleanUp;
        status = FMD_GetBlockStatus(pStore->pos.phBlock);
        if ((status & BLOCK_STATUS_BAD) == 0) pStore->pos.block++;
        }
    phBlock = pStore->pos.phBlock;

cleanUp:
    return phBlock;
}

//------------------------------------------------------------------------------

UINT32
MapLgToPhSector(
    NAND_STORE *pStore,
    UINT32 sector
    )
{
    UINT32 phSector = INVALID_SECTOR;
    UINT32 sectorInBlock, block, phBlock;

    block = sector / pStore->sectorsPerBlock;
    sectorInBlock = sector - block * pStore->sectorsPerBlock;
    phBlock = MapLgToPhBlock(pStore, block);
    if (phBlock == INVALID_BLOCK) goto cleanUp;

    phSector = phBlock * pStore->sectorsPerBlock + sectorInBlock;

cleanUp:
    return phSector;
}

//------------------------------------------------------------------------------

BOOL
ReadSectorsDirect(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT8* pData,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 phSector;

    sector += pRegion->block * pStore->sectorsPerBlock;

    while (sectors-- > 0)
        {
        // Get physical sector
        phSector = MapLgToPhSector(pStore, sector);
        if (phSector == INVALID_SECTOR)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!ReadSectors: "
                L"MapLgToPhSector for logical sector %d failed\r\n", sector
                ));
            goto cleanUp;
            }

        // Read sector data
        if (!FMD_ReadSector(phSector, pData, NULL, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!ReadSectors: "
                L"Sector read failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }
        // Move data pointers
        pData += pStore->sectorSize;
        sector++;
        }

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
ReadSectorsMapped(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT8* pData,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 lgSector, lgFirst, lgLast, lgEmpty;
    UINT32 phSector;
    UINT32 id;
    SectorInfo info;
    UINT8 *pBuffer;
//    UINT16 sectorFlags;


    // Initialize used and empty sectors info
    lgFirst = INVALID_SECTOR;
    lgLast = INVALID_SECTOR;
    lgEmpty = INVALID_SECTOR;

    // Parse all used sectors on region
    for (
        lgSector = pRegion->firstLgSector; 
        lgSector <= pRegion->lastLgSector;
        lgSector++
            )
        {
        // No need continue when all sectors are read
        if (sectors == 0) break;
        
        // Get physical sector address
        phSector = MapLgToPhSector(pStore, lgSector);

        if( phSector == INVALID_SECTOR ){ 
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!ReadSectorsMapped: "
                L"Reporting invalid sector %d!\r\n", phSector
                ));

            }

        // Read sector info
        if (!FMD_ReadSector(phSector, NULL, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!ReadSectorsMapped: "
                L"Sector info read failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        // If sector is empty try next one...
        if (info.wReserved2 == 0xFFFF)
            {
            if (lgEmpty == INVALID_SECTOR) lgEmpty = lgSector;
            continue;
            }

        // Update first and last used sector
        if (lgFirst == INVALID_SECTOR) lgFirst = lgSector;
        lgLast = lgSector;

        // If sector is dirty, try next one...
        if ((info.wReserved2 & 0x0001) != 0x0001) continue;

        // If write is in progress and it isn't done, try next one..
        if ((info.wReserved2 & 0x0006) != 0x0000) {
            OALMSG(1, (L"Write in progress for LgSector 0x%x\r\n", lgSector));
            continue;
            }

        // Is this sector in range we want read...
        id = info.dwReserved1 - pRegion->falBaseSector;
        if ((id < sector) || (id >= (sector + sectors))) continue;
            

        // Calculate target buffer pointer
        pBuffer = pData + (id - sector) * pStore->sectorSize;

        // Read sector data
        if (!FMD_ReadSector(phSector, pBuffer, NULL, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!ReadSectorsMapped: "
                L"Sector read failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        sectors--;
        }

    // Update first and last sector used if possible
    if (lgFirst != INVALID_SECTOR) pRegion->firstLgSector = lgFirst;
    if (sectors > 0) pRegion->lastLgSector = lgLast;
    if (lgEmpty < pRegion->emptyLgSector) pRegion->emptyLgSector = lgEmpty;
    
    // Done
    rc = (sectors == 0);

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
ReadSectors(
    NAND_STORE *pStore,
    UINT32 sector,
    UINT8* pData,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    NAND_REGION *pRegion = &pStore->region[pStore->openRegion];

    switch (pRegion->type)
        {
        case REGION_TYPE_BIN:
        case REGION_TYPE_RESERVED:
        case REGION_TYPE_XIP:
            rc = ReadSectorsDirect(pStore, pRegion, sector, pData, sectors);
            break;
        case REGION_TYPE_RO:
        case REGION_TYPE_RW:
            rc = ReadSectorsMapped(pStore, pRegion, sector, pData, sectors);
            break;
        }
    return rc;
}

//------------------------------------------------------------------------------

#ifndef STORE_READONLY

BOOL
AreSectorsEmpty(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 phSector;
    SectorInfo info;


    // Sector number can be region relative
    if (pRegion != NULL) sector += pRegion->block * pStore->sectorsPerBlock;

    // Check all sectors
    while (sectors-- > 0)
        {
        // Get physical sector
        phSector = MapLgToPhSector(pStore, sector);
        if (phSector == INVALID_SECTOR)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!AreSectorsEmpy: "
                L"MapLgToPhSector for logical sector %d failed!\r\n", sector
                ));
            goto cleanUp;
            }
        // Read sector information
        if (!FMD_ReadSector(phSector, NULL, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!AreSectorsEmpy: "
                L"Sector read failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }
        // Sector info Reserved2 is changed when sector isn't erased
        if ((info.wReserved2 != 0xFFFF) || (info.bOEMReserved != 0xFF))
            goto cleanUp;
        // Move to next sector
        sector++;
        }

    // Done, sector is empty
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
UpdateSectorsOemByte(
    NAND_STORE *pStore,
    UINT32 sector,
    UINT32 sectors,
    UINT8 oemByte
    )
{
    BOOL rc = FALSE;
    UINT32 phSector;
    SectorInfo info;
    UINT8 newOemByte;


    while (sectors-- > 0)
        {
        // Get physical sector
        phSector = MapLgToPhSector(pStore, sector);
        if (phSector == INVALID_SECTOR)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!UpdateSectorsOemByte: "
                L"MapLgToPhSector for logical sector %d failed!\r\n", sector
                ));
            goto cleanUp;
            }
        // Read sector information
        if (!FMD_ReadSector(phSector, NULL, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!UpdateSectorsOemByte: "
                L"Sector read failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }
        newOemByte = info.bOEMReserved & oemByte;
        if (newOemByte != oemByte)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!UpdateSectorsOemByte: "
                L"Unable set OEM byte to 0x%02x (value 0x%02x, sector %d)!\r\n",
                oemByte, newOemByte, phSector
                ));
            goto cleanUp;
            }
        info.bOEMReserved = newOemByte;
        if (!FMD_WriteSector(phSector, NULL, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!UpdateSectorsOemByte: "
                L"Sector write failde for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        // Move to next sector
        sector++;
        }

    // Done, sector is empty
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
EraseBlocks(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 block,
    UINT32 blocks
    )
{
    BOOL rc = FALSE;
    UINT32 phBlock;

    // Block number can be region relative
    if (pRegion != NULL) block += pRegion->block;
    
    while (blocks-- > 0)
        {
        phBlock = MapLgToPhBlock(pStore, block);
        if (phBlock == INVALID_BLOCK)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!EraseBlocks: "
                L"GetPhBlock for logical block %d failed!\r\n", block
                ));
            goto cleanUp;
            }
        if (!FMD_EraseBlock(phBlock))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!EraseBlocks: "
                L"Block erase failed for block %d!\r\n", phBlock
                ));
            goto cleanUp;
            }

        block++;
        }

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
WriteSectorsDirect(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT8* pData,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 lgSector, phSector;
    UINT8 oemByte;
    SectorInfo sectorInfo;


    // Sector number is region relative
    lgSector = pRegion->block * pStore->sectorsPerBlock + sector;
    switch (pRegion->type)
        {
        case REGION_TYPE_BIN:
            oemByte = ~0x03;
            break;
        default:
            oemByte = 0xFF;
            break;
        }            
    
    // Read all sectors...
    while (sectors-- > 0)
        {

        // Get physical sector
        phSector = MapLgToPhSector(pStore, lgSector);
        if (phSector == INVALID_SECTOR)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteSectorsDirect: "
                L"GetPhSector for logical sector %d failed!\r\n", sector
                ));
            goto cleanUp;
            }

        // Create sector info
        sectorInfo.bOEMReserved = oemByte;
        sectorInfo.wReserved2 = ~0x0006;
        sectorInfo.bBadBlock = 0xFF;
        if (pRegion->falBaseSector == INVALID_SECTOR)
            {
            sectorInfo.dwReserved1 = 0xFFFFFFFF;
            }
        else
            {
            sectorInfo.dwReserved1 = pRegion->falBaseSector + sector;
            }

        if (!FMD_WriteSector(phSector, pData, &sectorInfo, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteSectors: "
                L"Sector write failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        // Move data pointers
        pData += pStore->sectorSize;
        lgSector++;
        sector++;
        }

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
WriteSectorsMapped(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT8* pData,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 lgSector, lgLast, lgEmpty;
    UINT32 phSector;
    SectorInfo info;


    lgEmpty = pRegion->emptyLgSector;
    lgSector = pRegion->block * pStore->sectorsPerBlock;
    lgLast = lgSector + pRegion->blocks * pStore->sectorsPerBlock - 1;

    // Read all sectors plus one more pass for empty one
    while (sectors >= 0)
        {
        // Look for empty sector
        if (lgEmpty == INVALID_SECTOR)
            {
            while (lgSector <= lgLast)
                {
                // Get physical sector address
                phSector = MapLgToPhSector(pStore, lgSector);
                // Read sector info
                if (!FMD_ReadSector(phSector, NULL, &info, 1))
                    {
                    OALMSG(OAL_ERROR, (
                        L"ERROR: BootStoreNand!WriteSectorsMapped: "
                        L"Sector info read failed for sector %d!\r\n", phSector
                        ));
                    goto cleanUp;
                    }
                // If sector isn't empty, try next one...
                if (info.wReserved2 == 0xFFFF)
                    {
                    lgEmpty = lgSector;
                    break;
                    }
                // Try next sector
                lgSector++;
                }
            }
        else
            {
            phSector = MapLgToPhSector(pStore, lgEmpty);
            }

        // Exit when we wrote all sectors already
        if (sectors == 0)
            {
            pRegion->emptyLgSector = lgEmpty;
            break;
            }
        
        // If there isn't empty sector we have to fail
        if (lgEmpty == INVALID_SECTOR)
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteSectorsMapped: "
                L"No empty sector find on region!\r\n"
                ));
            goto cleanUp;
            }
        
        // Create sector info
        info.bBadBlock = 0xFF;
        info.dwReserved1 = sector + pRegion->falBaseSector;
        info.wReserved2 = ~0x0006;
        info.bOEMReserved = 0xFF;
        
        // Write sector data and info
        if (!FMD_WriteSector(phSector, pData, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteSectorsMapped: "
                L"Sector write failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        // Update first/last used sector
        if (pRegion->firstLgSector == INVALID_SECTOR)
            {
            pRegion->firstLgSector = lgEmpty;
            pRegion->lastLgSector = lgEmpty;
            }
        else if (lgEmpty < pRegion->firstLgSector)
            {
            pRegion->firstLgSector = lgEmpty;
            }
        else if (lgEmpty > pRegion->lastLgSector)
            {
            pRegion->lastLgSector = lgEmpty;
            }
        if (pRegion->firstSector == INVALID_SECTOR)
            {
            pRegion->firstSector = sector;
            pRegion->lastSector = sector;
            }
        else if (sector < pRegion->firstSector)
            {
            pRegion->firstSector = sector;
            }
        else if (sector > pRegion->lastSector)
            {
            pRegion->lastSector = sector;
            }
        
        // Update sector number & empty for next sector
        lgSector = lgEmpty + 1;
        lgEmpty = INVALID_SECTOR;
        
        // Move to next sector...
        sector++;
        pData += pStore->sectorSize;
        sectors--;
        }

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
EraseSectorsDirect(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 sectorsPerBlock;
    UINT32 posInBlock;
    UINT32 blocks;


    sectorsPerBlock = pStore->sectorsPerBlock;
    posInBlock = sector % sectorsPerBlock;
    if (posInBlock > 0)
        {
        UINT32 sectorsInBlock = sectorsPerBlock - posInBlock;
        if (sectorsInBlock > sectors) sectorsInBlock = sectors;
        if (!AreSectorsEmpty(pStore, pRegion, sector, sectorsInBlock))
            {
            UINT32 startSector = sector - posInBlock;

            // Read data at block beginning
            if (!ReadSectorsDirect(
                    pStore, pRegion, startSector, pStore->buffer, posInBlock
                    ))
                goto cleanUp;
            // Erase block
            if (!EraseBlocks(pStore, pRegion, startSector/sectorsPerBlock, 1))
                goto cleanUp;
            // Write data back
            if (!WriteSectorsDirect(
                    pStore, pRegion, startSector, pStore->buffer, posInBlock
                    ))
                goto cleanUp;
            }
        sector += sectorsInBlock;
        sectors -= sectorsInBlock;
        }

    // Erase full blocks
    blocks = sectors/sectorsPerBlock;
    if (!EraseBlocks(pStore, pRegion, sector/sectorsPerBlock, blocks))
        goto cleanUp;

    sector += blocks * sectorsPerBlock;
    sectors -= blocks * sectorsPerBlock;

    // And we may need erase part of last block
    if (sectors > 0)
        {
        if (!AreSectorsEmpty(pStore, pRegion, sector, sectors))
            {
            // Read data at end of block
            if (!ReadSectorsDirect(
                    pStore, pRegion, sector + sectors, pStore->buffer,
                    sectorsPerBlock - sectors
                    ))
                goto cleanUp;
            // Erase block
            if (!EraseBlocks(pStore, pRegion, sector/sectorsPerBlock, 1))
                goto cleanUp;
            // Write data back
            if (!WriteSectorsDirect(
                    pStore, pRegion, sector + sectors, pStore->buffer, 
                    sectorsPerBlock - sectors
                    ))
                goto cleanUp;
            }
        sector += sectors;
        sectors = 0;
        }

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
EraseSectorsMapped(
    NAND_STORE *pStore,
    NAND_REGION *pRegion,
    UINT32 sector,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    UINT32 first, last;
    UINT32 lgSector, lgFirst, lgLast, lgEmpty;
    UINT32 phSector;
    UINT32 id;
    SectorInfo info;


    // Initialize sector pointers
    lgFirst = INVALID_SECTOR;
    lgLast = INVALID_SECTOR;
    lgEmpty = INVALID_SECTOR;
    first = INVALID_SECTOR;
    last = INVALID_SECTOR;

    // Reduce erase to sector numbers which may exist
    if (pRegion->firstSector == INVALID_SECTOR)
        {
        sectors = 0;
        }
    else if (pRegion->firstSector > sector)
        {
        if (sectors > (sector - pRegion->firstSector))
            {
            sectors -= sector - pRegion->firstSector;
            sector = pRegion->firstSector;
            }
        else
            {
            sectors = 0;
            }
        }
    else if (pRegion->lastSector < (sector + sectors - 1))
        {
        sectors = pRegion->lastSector - sector + 1;
        }

    // Parse all used sectors on region
    for (
        lgSector = pRegion->firstLgSector; 
        lgSector <= pRegion->lastLgSector; 
        lgSector++
            )
        {
        
        // If there are no sectors to erase, exit
        if (sectors == 0) break;
        
        // Get physical sector address
        phSector = MapLgToPhSector(pStore, lgSector);

        // Read sector info
        if (!FMD_ReadSector(phSector, NULL, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!EraseSectorsMapped: "
                L"Sector info read failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        // If sector is empty, try next one...
        if (info.wReserved2 == 0xFFFF)
            {
            if (lgEmpty == INVALID_SECTOR) lgEmpty = lgSector;
            continue;
            }

        // Update first and last used sector
        if (lgFirst == INVALID_SECTOR) lgFirst = lgSector;
        lgLast = lgSector;
        
        // If sector is dirty, try next one...
        if ((info.wReserved2 & 0x0001) != 0x0001) continue;

        // If write is in progress and it isn't done, try next one..
        if ((info.wReserved2 & 0x0006) != 0x0000) continue;

        // Is this sector in range we want erase...
        id = info.dwReserved1 - pRegion->falBaseSector;
        if ((id < sector) || (id >= (sector + sectors)))
            {
            // It isn't, but it is valid sector
            if ((first == INVALID_SECTOR) || (id < first)) first = id;
            if ((last == INVALID_SECTOR) || (id > last)) last = id;
            continue;
            }

        // Mark sector as dirty
        info.wReserved2 &= ~0x0001;
        
        // Write sector info
        if (!FMD_ReadSector(phSector, NULL, &info, 1))
            {
            OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!EraseSectorsMapped: "
                L"Sector info write failed for sector %d!\r\n", phSector
                ));
            goto cleanUp;
            }

        // One less sector to erase
        sectors--;
        }

    // If we walk all partition
    if (lgSector > pRegion->lastLgSector)
        {
        pRegion->firstLgSector = lgFirst;
        pRegion->lastLgSector = lgLast;
        pRegion->firstSector = first;
        pRegion->lastSector = last;
        }
    else if (lgFirst != INVALID_SECTOR)
        {
        pRegion->firstLgSector = lgFirst;
        }

    // And finally empty sector
    if ((lgEmpty != INVALID_SECTOR) && (lgEmpty < pRegion->emptyLgSector))
        {
        pRegion->emptyLgSector = lgEmpty;
        }
    
    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
EraseSectors(
    NAND_STORE *pStore,
    UINT32 sector,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    NAND_REGION *pRegion = &pStore->region[pStore->openRegion];


    switch (pRegion->type)
        {
        case REGION_TYPE_BIN:
        case REGION_TYPE_RESERVED:
        case REGION_TYPE_XIP:
            rc = EraseSectorsDirect(pStore, pRegion, sector, sectors);
            break;
        case REGION_TYPE_RO:
        case REGION_TYPE_RW:
            rc = EraseSectorsMapped(pStore, pRegion, sector, sectors);
            break;
        }
    return rc;
}

//------------------------------------------------------------------------------

BOOL
WriteSectors(
    NAND_STORE *pStore,
    UINT32 sector,
    UINT8* pData,
    UINT32 sectors
    )
{
    BOOL rc = FALSE;
    NAND_REGION *pRegion = &pStore->region[pStore->openRegion];


    switch (pRegion->type)
        {
        case REGION_TYPE_BIN:
        case REGION_TYPE_RESERVED:
        case REGION_TYPE_XIP:
            rc = WriteSectorsDirect(pStore, pRegion, sector, pData, sectors);
            break;
        case REGION_TYPE_RO:
        case REGION_TYPE_RW:
            rc = WriteSectorsMapped(pStore, pRegion, sector, pData, sectors);
            break;
        }
    return rc;
}

#endif

//------------------------------------------------------------------------------

BOOL
OpenBinary(
    NAND_STORE* pStore,
    BOOT_STORE_OPEN_BINARY_REGION* pInfo
    )
{
    BOOL rc = FALSE;


    if ((pInfo->index >= dimof(pStore->region)) ||
        (pStore->region[pInfo->index].type != REGION_TYPE_BIN))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!OpenBinary: "
            L"Invalid binary region index %d\r\n", pInfo->index
            ));
        goto cleanUp;
        }

    // Ready for bussiness
    pStore->openMode = NAND_MODE_OPEN_BINARY;
    pStore->openRegion = pInfo->index;
    pStore->openBaseSector = 0;
    pStore->openSize = pStore->binSize[pInfo->index];
    pStore->openPos  = 0;

    // Move pointer
    pStore->pos.block = pStore->region[pInfo->index].block;
    pStore->pos.phBlock = pStore->region[pInfo->index].phBlock;

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
OpenReservedRegion(
    NAND_STORE* pStore,
    BOOT_STORE_OPEN_RESERVED_REGION* pInfo
    )
{
    BOOL rc = FALSE;
    UINT32 ix;


    // Look for region
    for (ix = 0; ix < pStore->regions; ix++)
        {
        if (pStore->region[ix].type != REGION_TYPE_RESERVED) continue;
        if (strcmp(pStore->region[ix].name, pInfo->name) == 0) {
            break;
            }
        }
    if (ix >= pStore->regions)
        {
        OALMSG(OAL_WARN, (L"NandStore!OpenReservedRegion: "
            L"Reserved region '%hs' wasn't found\r\n", pInfo->name
            ));
        goto cleanUp;
        }

    // Set open information
    pStore->openMode = NAND_MODE_OPEN_RESERVED;
    pStore->openRegion = ix;
    pStore->openBaseSector = 0;
    pStore->openSize = pStore->region[ix].blocks * pStore->blockSize;
    pStore->openPos  = 0;

    // Move pointer
    pStore->pos.block = pStore->region[ix].block;
    pStore->pos.phBlock = pStore->region[ix].phBlock;

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
OpenPartition(
    NAND_STORE* pStore,
    BOOT_STORE_OPEN_PARTITION* pInfo
    )
{
    BOOL rc = FALSE;
    UINT32 ix;


    for (ix = 0; ix < pStore->partitions; ix++)
        {
        if (pStore->partition[ix].type == pInfo->type) break;
        }
    if (ix >= pStore->partitions) goto cleanUp;

    // Set open information
    pStore->openMode = NAND_MODE_OPEN_PARTITION;
    pStore->openRegion = pStore->partition[ix].region;
    pStore->openBaseSector = pStore->partition[ix].offset;
    pStore->openSize = pStore->partition[ix].sectors * pStore->sectorSize;
    pStore->openPos = 0;

    // Move pointer
    pStore->pos.block = pStore->region[pStore->openRegion].block;
    pStore->pos.phBlock = pStore->region[pStore->openRegion].phBlock;

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
GetSize(
    NAND_STORE* pStore,
    BOOT_STORE_GETSIZE_PARAMS *pParams
    )
{
    BOOL rc = FALSE;

    // Returned value depends on openMode
    switch (pStore->openMode)
        {
        case NAND_MODE_INIT:
            pParams->size = pStore->phBlocksOnFlash * pStore->blockSize;
            break;
        case NAND_MODE_OPEN_BINARY:
        case NAND_MODE_OPEN_RESERVED:
        case NAND_MODE_OPEN_PARTITION:
            pParams->size = pStore->openSize;
            break;
        default:
            goto cleanUp;
        }

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
Seek(
    NAND_STORE* pStore,
    BOOT_STORE_SEEK_PARAMS *pParams
    )
{
    BOOL rc = FALSE;

    // We support only 32-bit seek
    if ((pParams->position >> 32) != 0)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Seek: "
            L"Seek behind media size!\r\n"
            ));
        goto cleanUp;
        }

    // Seek only in open space position
    if (pParams->position > pStore->openSize)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Seek: "
            L"Seek behind media size!\r\n"
            ));
        goto cleanUp;
        }

    // Seek depends on open mode
    switch (pStore->openMode)
        {
        case NAND_MODE_OPEN_BINARY:
        case NAND_MODE_OPEN_RESERVED:
        case NAND_MODE_OPEN_PARTITION:
            pStore->openPos = (UINT32)pParams->position;
            break;
        default:
            goto cleanUp;
        }

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
GetPos(
    NAND_STORE* pStore,
    BOOT_STORE_GETPOS_PARAMS *pParams
    )
{
    BOOL rc = FALSE;

    // Seek depends on open mode
    switch (pStore->openMode)
        {
        case NAND_MODE_OPEN_BINARY:
        case NAND_MODE_OPEN_RESERVED:
        case NAND_MODE_OPEN_PARTITION:
            pParams->position = pStore->openPos;
            break;
        default:
            goto cleanUp;
        }

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
Read(
    NAND_STORE *pStore,
    UCHAR* pBuffer,
    UINT32 size
    )
{
    BOOL rc = FALSE;
    UINT32 sectorSize;
    UINT32 pos, sector, posInSector;
    UINT32 sectors;

    // Shortcuts
    sectorSize = pStore->sectorSize;

    // Sector to start read & amount to read
    pos = pStore->openBaseSector * sectorSize + pStore->openPos;
    sector = pos / sectorSize;
    posInSector = pos - sector * sectorSize;
    if (posInSector > 0)
        {
        UINT8 buffer[SECTOR_MAX_SIZE];
        UINT32 amount = sectorSize - posInSector;
        if (amount > size) amount = size;
        if (!ReadSectors(pStore, sector, buffer, 1)) goto cleanUp;
        memcpy(pBuffer, &buffer[posInSector], amount);
        pStore->openPos += amount;
        pBuffer += amount;
        size -= amount;
        sector++;
        }

    // Read full sectors
    sectors = size / sectorSize;
    if (sectors > 0)
        {
        UINT32 amount = sectors * sectorSize;
        if (!ReadSectors(pStore, sector, pBuffer, sectors)) goto cleanUp;
        pStore->openPos += amount;
        pBuffer += amount;
        size -= amount;
        sector += sectors;
        }

    // Should we read partial sector at end?
    if (size > 0)
        {
        UINT8 buffer[SECTOR_MAX_SIZE];
        if (!ReadSectors(pStore, sector, buffer, 1)) goto cleanUp;
        memcpy(pBuffer, buffer, size);
        pStore->openPos += size;
        pBuffer += size;
        size = 0;
        }

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

#ifndef STORE_READONLY

BOOL
Write(
    NAND_STORE* pStore,
    UCHAR* pBuffer,
    UINT32 size
    )
{
    BOOL rc = FALSE;
    UINT32 sectorSize, sectors;
    UINT32 pos, sector, firstSector, lastSector;
    UINT8 *pFirst = pStore->firstSector;
    UINT8 *pLast = pStore->lastSector;
    UINT32 posInFirst, posInLast;


    // Shortcut
    sectorSize = pStore->sectorSize;

    // Get amount to write in first and last sector
    pos = pStore->openBaseSector * sectorSize + pStore->openPos;
    firstSector = pos / sectorSize;
    posInFirst = pos - firstSector * sectorSize;
    lastSector = (pos + size - 1) / sectorSize;
    posInLast = pos + size - lastSector * sectorSize;
    if ((posInLast >= sectorSize) || (firstSector == lastSector)) posInLast = 0;

    // Read first and last sector
    if (posInFirst > 0)
        {
        if (!ReadSectors(pStore, firstSector, pFirst, 1)) goto cleanUp;
        }
    if (posInLast > 0)
        {
        if (!ReadSectors(pStore, lastSector, pLast, 1)) goto cleanUp;
        }

    // Get number of sectors to erase
    sectors = lastSector - firstSector + 1;

    // Erase
    sector = firstSector;
    if (!EraseSectors(pStore, sector, sectors)) goto cleanUp;

    // Write first sector
    if (posInFirst > 0)
        {
        UINT32 amount = sectorSize - posInFirst;
        if (amount > size) amount = size;
        memcpy(&pFirst[posInFirst], pBuffer, amount);
        if (!WriteSectors(pStore, sector, pFirst, 1)) goto cleanUp;
        pStore->openPos += amount;
        pBuffer += amount;
        size -= amount;
        sector++;
        }

    // Then full sectors
    sectors = size / sectorSize;
    if (sectors > 0)
        {
        UINT32 amount = sectors * sectorSize;
        if (!WriteSectors(pStore, sector, pBuffer, sectors)) goto cleanUp;
        pStore->openPos += amount;
        pBuffer += amount;
        size -= amount;
        sector += sectors;
        }

    // And last sector
    if (size > 0)
        {
        memcpy(pLast, pBuffer, size);
        if (!WriteSectors(pStore, sector, pLast, 1)) goto cleanUp;
        pStore->openPos += size;
        pBuffer += size;
        size = 0;
        sector++;
        }

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

#endif

//------------------------------------------------------------------------------

#ifndef STORE_READONLY

BOOL
AddPartitionByType(
    BOOT_STORE_FORMAT_INFO* pInfo,
    UINT32 type,
    PARTENTRY table[4],
    UINT32 *pPartitions,
    UINT32 sectorSize,
    UINT32 *pSector,
    UINT32 *pSectors
    )
{
    BOOL rc = FALSE;
    UINT32 partition, sectors;
    BOOT_STORE_PARTITION_INFO *pPartition;

    // We can't add partition if we run out of media of table
    if ((*pPartitions >= 4) || (*pSectors == 0)) goto cleanUp;

    for (partition = 0; partition < pInfo->partitions; partition++)
        {
        if (pInfo->pPartitionInfo[partition].type == type)
            {
            pPartition = &pInfo->pPartitionInfo[partition];
            sectors = (UINT32)((pPartition->size + sectorSize - 1)/sectorSize);
            if (sectors > *pSectors) sectors = *pSectors;
            table[*pPartitions].Part_FileSystem = (UINT8)type;
            table[*pPartitions].Part_StartSector = *pSector;
            table[*pPartitions].Part_TotalSectors = sectors;
            (*pPartitions)++;
            *pSector += sectors;
            *pSectors -= sectors;
            partition++;
            break;
            }
        }

    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
CreateMbrAndLayout(
    NAND_STORE* pStore,
    BOOT_STORE_FORMAT_INFO* pInfo,
    UINT8* pMbrSector,
    UINT8* pLayoutSector
    )
{
    BOOL rc = FALSE;
    FlashLayoutSector* pLayout;
    ReservedEntry* pReservedEntry;
    FlashRegion* pFlashRegion;
    PARTENTRY table[4];
    UINT32 reservedRegions, region;
    UINT32 partitions, partition;
    UINT32 xipSize, roSize, rwSize;
    BOOT_STORE_RESERVED_REGION_INFO *pRegion;
    BOOT_STORE_PARTITION_INFO *pPartition;
    UINT32 blockSize, sectorSize;
    UINT32 block, blocks, sector, sectors;


    // Shortcuts
    reservedRegions = pInfo->reservedRegions;
    partitions = pInfo->partitions;
    blockSize = pStore->blockSize;
    sectorSize = pStore->sectorSize;

    // First start with layout sector
    memset(pLayoutSector, 0xFF, pStore->sectorSize);
    pLayout = (FlashLayoutSector*)pLayoutSector;
    memcpy(pLayout->abFLSSig, FLASH_LAYOUT_SIG, FLASH_LAYOUT_SIG_SIZE);

    pLayout->cbReservedEntries = sizeof(ReservedEntry) * reservedRegions;
    pReservedEntry = (ReservedEntry*)(
        &pLayoutSector[sizeof(FlashLayoutSector)]
        );
    pLayout->cbRegionEntries = 0;
    pFlashRegion = (FlashRegion*)(
        &pLayoutSector[sizeof(FlashLayoutSector) + pLayout->cbReservedEntries]
        );

    // Number blocks on flash for FAL
    blocks = pStore->blocksOnFlash - pStore->mbrBlock;

    // Calculate size for XIP region
    xipSize = 2;
    pPartition = pInfo->pPartitionInfo;
    for (partition = 0; partition < partitions; partition++, pPartition++)
        {
        switch (pPartition->type)
            {
            case PART_BOOTSECTION:
            case PART_RAMIMAGE:
                sectors = (UINT32)(
                    (pPartition->size + sectorSize - 1)/sectorSize
                    );
                xipSize += sectors * sectorSize;
                break;
            }
        }
    xipSize = (xipSize + blockSize - 1) / blockSize;
    if (xipSize > blocks) goto cleanUp;
    blocks -= xipSize;
    xipSize *= blockSize;

    // Now for RO region
    roSize = 0;
    pPartition = pInfo->pPartitionInfo;
    for (partition = 0; partition < partitions; partition++, pPartition++)
        {
        switch (pPartition->type)
            {
            case PART_IMGFS:
                sectors = (UINT32)(
                    (pPartition->size + sectorSize - 1)/sectorSize
                    );
                roSize += sectors * sectorSize;
                break;
            }
        }
    roSize = (roSize + blockSize - 1) / blockSize;
    if ((roSize + 2) > blocks) goto cleanUp;
    blocks -= roSize + 2;
    roSize *= blockSize;

    // Now RW region - rest of media
    if (2 > blocks) goto cleanUp;
    rwSize = (blocks - 2) * blockSize;

    // Fill flash regions info
    if (xipSize > 0)
        {
        memset(pFlashRegion, 0, sizeof(FlashRegion));
        pFlashRegion->regionType = XIP;
        pFlashRegion->dwNumLogicalBlocks = xipSize / blockSize;
        pFlashRegion->dwSectorsPerBlock = pStore->sectorsPerBlock;
        pFlashRegion->dwBytesPerBlock = blockSize;
        pFlashRegion->dwCompactBlocks = 0;
        pLayout->cbRegionEntries += sizeof(FlashRegion);
        pFlashRegion++;
        }
    if (roSize > 0)
        {
        memset(pFlashRegion, 0, sizeof(FlashRegion));
        pFlashRegion->regionType = READONLY_FILESYS;
        pFlashRegion->dwNumLogicalBlocks = roSize / blockSize;
        pFlashRegion->dwSectorsPerBlock = pStore->sectorsPerBlock;
        pFlashRegion->dwBytesPerBlock = blockSize;
        pFlashRegion->dwCompactBlocks = 2;
        pLayout->cbRegionEntries += sizeof(FlashRegion);
        pFlashRegion++;
        }
    if (rwSize > 0)
        {
        memset(pFlashRegion, 0, sizeof(FlashRegion));
        pFlashRegion->regionType = FILESYS;
        pFlashRegion->dwNumLogicalBlocks = 0xFFFFFFFF;
        pFlashRegion->dwSectorsPerBlock = pStore->sectorsPerBlock;
        pFlashRegion->dwBytesPerBlock = blockSize;
        pFlashRegion->dwCompactBlocks = 2;
        pLayout->cbRegionEntries += sizeof(FlashRegion);
        pFlashRegion++;
        }

    // Fill reserved region info
    block = 0;
    pRegion = pInfo->pReservedRegionInfo;
    for (region = 0; region < pInfo->reservedRegions; region++)
        {
        memset(&pReservedEntry[region], 0, sizeof(ReservedEntry));
        blocks = (pRegion[region].size + blockSize - 1)/blockSize;
        strcpy(pReservedEntry[region].szName, pRegion[region].name);
        pReservedEntry[region].dwStartBlock = block;
        pReservedEntry[region].dwNumBlocks = blocks;
        block += blocks;
        }

    // Clean partition table
    memset(table, 0, sizeof(table));
    partitions = 0;

    // Add partitions on XIP flash region
    sector = 2;
    sectors = (xipSize - 2) / sectorSize;
    AddPartitionByType(
        pInfo, PART_BOOTSECTION, table, &partitions, sectorSize,
        &sector, &sectors
        );
    AddPartitionByType(
        pInfo, PART_RAMIMAGE, table, &partitions, sectorSize,
        &sector, &sectors
        );

    // Add partitions on RO flash region
    sector = xipSize / sectorSize;
    sectors = roSize / sectorSize;
    AddPartitionByType(
        pInfo, PART_IMGFS, table, &partitions, sectorSize,
        &sector, &sectors
        );

    // Add partitions on RW flash region
    sector = (xipSize + roSize) / sectorSize;
    sectors = rwSize / sectorSize;
    AddPartitionByType(
        pInfo, PART_DOS3_FAT, table, &partitions,
        sectorSize, &sector, &sectors
        );
    AddPartitionByType(
        pInfo, PART_DOS4_FAT, table, &partitions,
        sectorSize, &sector, &sectors
        );

    // Create MBR layout
    memset(pMbrSector, 0xFF, pStore->sectorSize);
    pMbrSector[0] = BS3BYTJMP;
    *(UINT16*)&pMbrSector[510] = BOOTSECTRAILSIGH;
    memcpy(&pMbrSector[510 - sizeof(table)], table, sizeof(table));

    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

//------------------------------------------------------------------------------

BOOL
WriteMbrAndLayout(
    NAND_STORE* pStore,
    UINT8* pMbrSector,
    UINT8* pLayoutSector
    )
{
    BOOL rc = FALSE;
    UINT32 sector, phSector;
    SectorInfo sectorInfo;


    // Get physical sector
    sector = pStore->mbrBlock * pStore->sectorsPerBlock;
    phSector = MapLgToPhSector(pStore, sector);
    if (phSector == INVALID_SECTOR)
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteMbrAndLayout: "
            L"GetPhSector for logical sector %d failed!\r\n", sector
            ));
        goto cleanUp;
        }

    sectorInfo.bOEMReserved = 0xFF;
    sectorInfo.dwReserved1 = 0;
    sectorInfo.wReserved2 = ~0x0006;
    sectorInfo.bBadBlock = 0xFF;
    if (!FMD_WriteSector(phSector, pMbrSector, &sectorInfo, 1))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteMbrAndLayout: "
            L"Sector write failed for MBR sector %d!\r\n", phSector
            ));
        goto cleanUp;
        }

    OALMSG(TRUE, (L"MBR written to sector #%d!\r\n", phSector));
    
    sectorInfo.bOEMReserved = 0xFF;
    sectorInfo.dwReserved1 = 1;
    sectorInfo.wReserved2 = ~0x0006;
    sectorInfo.bBadBlock = 0xFF;
    if (!FMD_WriteSector(phSector + 1, pLayoutSector, &sectorInfo, 1))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!WriteMbrAndLayout: "
            L"Sector write failed for Layout sector %d!\r\n", phSector
            ));
        goto cleanUp;
        }

    rc = TRUE;

cleanUp:
    return rc;
    
}

//------------------------------------------------------------------------------

BOOL
Format(
    NAND_STORE* pStore,
    BOOT_STORE_FORMAT_INFO* pInfo
    )
{
    BOOL rc = FALSE;
    UINT32 block, blocks;
    UINT32 blockSize, sectorSize;
    UINT8 *pMbr = pStore->firstSector;
    UINT8 *pLayout = pStore->lastSector;
    UINT32 region;


    if (((pInfo->binaryRegions > 0)&&(pInfo->pBinaryRegionInfo == NULL)) ||
        ((pInfo->reservedRegions > 0)&&(pInfo->pReservedRegionInfo == NULL)) ||
        ((pInfo->partitions > 0)&&(pInfo->pPartitionInfo == NULL)))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Format: "
            L"Invalid format info structure values!\r\n"
            ));
        goto cleanUp;
        }

    // Shortcuts
    blockSize = pStore->blockSize;
    sectorSize = pStore->sectorSize;
    
    // If we have format full storage update binary region blocks
    if ((pInfo->flags & BOOT_STORE_FORMAT_SKIP_BINARY_REGIONS) == 0)
        {
        blocks = 0;
        for (region = 0; region < pInfo->binaryRegions; region++)
            {
            UINT32 size = pInfo->pBinaryRegionInfo[region].size;
            blocks += (size + blockSize - 1)/blockSize;
            }
        pStore->binBlocks = blocks;
        }

    // Calculate MBR & layout position
    blocks = 0;
    for (region = 0; region < pInfo->reservedRegions; region++)
        {
        UINT32 size = pInfo->pReservedRegionInfo[region].size;
        blocks += (size + blockSize - 1)/blockSize;
        }
    pStore->mbrBlock = pStore->binBlocks + blocks;

    // Create MBR & layout sectors
    if (!CreateMbrAndLayout(pStore, pInfo, pMbr, pLayout))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Format: "
            L"Failed create MBR and Flash Layout sectors!\r\n"
            ));
        goto cleanUp;
        }

    // Get start block for erase
    if ((pInfo->flags & BOOT_STORE_FORMAT_SKIP_BINARY_REGIONS) != 0)
        {
        block = pStore->binBlocks;
        }
    else
        {
        block = 0;
        }

    // And erase them
    if (!EraseBlocks(pStore, NULL, block, pStore->blocksOnFlash - block))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Format: "
            L"Failed erase media!\r\n"
            ));
        goto cleanUp;
        }

    // Write Mbr & layout sectors
    if (!WriteMbrAndLayout(pStore, pMbr, pLayout))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Format: "
            L"Failed write MBR/Layout sectors!\r\n"
            ));
        goto cleanUp;
        }

    // Update OEM byte for binary regions
    if (!UpdateSectorsOemByte(
            pStore, 0, pStore->binBlocks * pStore->sectorsPerBlock, 0
            ))
        {
        OALMSG(OAL_ERROR, (L"ERROR: BootStoreNand!Format: "
            L"Failed set reserved attribute for binary partitions!\r\n"
            ));
        goto cleanUp;
        }

    // Re-mount store after layout change
    if (!MountStore(pStore)) goto cleanUp;

    // As we know store is empty now, clear few stuff
    for (region = 0; region < pStore->regions; region++)
        {
        switch (pStore->region[region].type)
            {
            case REGION_TYPE_RO:
            case REGION_TYPE_RW:
                pStore->region[region].firstLgSector = INVALID_SECTOR;
                pStore->region[region].lastLgSector = INVALID_SECTOR;
                pStore->region[region].firstSector = INVALID_SECTOR;
                pStore->region[region].lastSector = INVALID_SECTOR;
                break;
            }                
        }
    
    // Done
    rc = TRUE;

cleanUp:
    return rc;
}

#endif

//------------------------------------------------------------------------------

